package com.golte.exception;

import com.alibaba.fastjson.JSON;

import com.aliyun.oss.common.utils.StringUtils;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.golte.common.GlobalResponse;
import lombok.extern.slf4j.Slf4j;

import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.sql.SQLException;
import java.util.*;


/**
 * @version V1.0
 * @description: 全局异常拦截
 * @author: chenhuan
 * @date: 2021/5/26
 **/
@ControllerAdvice
@RestController
@Slf4j
public class GlobalExceptionHandler {

    /**
     * @description: 非法参数验证异常
     * @author: chenhuan
     * @date: 2021/5/28
     * @version V1.0
     **/
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(value = HttpStatus.OK)
    public GlobalResponse handleMethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
        log.warn("MethodArgumentNotValidException:" + ex.getMessage());
        BindingResult bindingResult = ex.getBindingResult();
        List<String> list = new ArrayList<>();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            list.add(fieldError.getDefaultMessage());
        }
        Collections.sort(list);
        log.error("fieldErrors" + JSON.toJSONString(list));
        if (!CollectionUtils.isEmpty(list)) {
            // return ApiResult.fail(String.join(";", list));
            return GlobalResponse.fail(String.join(";", list));
        }
        return GlobalResponse.fail("参数错误！");
    }


    /**
     * @description: 系统登录异常处理
     * @author: chenhuan
     * @date: 2021/5/28
     * @version V1.0
     **/
    @ExceptionHandler(value = SysLoginException.class)
    @ResponseStatus(HttpStatus.OK)
    public GlobalResponse sysLoginExceptionHandler(SysLoginException exception) {
        log.warn("SysLoginException:" + exception.getMessage());
        return GlobalResponse.fail("系统登录异常:" + exception.getMessage());
    }


    /**
     * @description: 解析具体字段
     * @author: chenhuan
     * @date: 2021/5/28
     * @version V1.0
     **/
    String findFieldName(HttpMessageNotReadableException exception) {
        StringBuffer fieldName = new StringBuffer();
        //解析类型出错字段，异常内部处理
        try {
            String exceptionMessage = exception.getMessage();
            if (!StringUtils.isNullOrEmpty(exceptionMessage)) {
                fieldName.append(exceptionMessage.substring(exceptionMessage.lastIndexOf("[\"") + 2, exceptionMessage.lastIndexOf("\"]")));
            }
        } catch (Exception ex) {

        }

        return fieldName.toString();
    }

    /**
     * @description: HttpMessageNotReadableException异常
     * @author: chenhuan
     * @date: 2021/5/28
     * @version V1.0
     **/
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.OK)
    public GlobalResponse httpMessageNotReadableException(HttpMessageNotReadableException exception) {
        log.error("HttpMessageNotReadableException:", exception);

        //获取异常类型
        Class excClass = exception.getCause().getClass();

        //1、判断JsonMappingException异常类型
        if (ClassUtils.isAssignable(excClass, JsonMappingException.class)) {
            return GlobalResponse.fail("JSON报文字段转换出错，类型不兼容！");
        }

        //2、判断JsonParseException异常类型
        if (ClassUtils.isAssignable(excClass, JsonParseException.class)) {
            return GlobalResponse.fail("JSON报文格式错误！");
        }

        //3、判断InvalidFormatException异常类型
        if (ClassUtils.isAssignable(excClass, InvalidFormatException.class)) {
            return GlobalResponse.fail(findFieldName(exception) + "字段类型不匹配！");
        }

        //4、其他Json异常类型
        return GlobalResponse.fail("JSON数据传输异常，请联系相关人员！");
    }

    /**
     * @description: HttpMediaTypeException异常处理
     * @author: chenhuan
     * @date: 2021/5/28
     * @version V1.0
     **/
    @ExceptionHandler(value = HttpMediaTypeException.class)
    @ResponseStatus(HttpStatus.OK)
    public GlobalResponse httpMediaTypeException(HttpMediaTypeException exception) {
        log.error("HttpMediaTypeException:", exception);
        return GlobalResponse.fail("JSON数据传输异常！");
    }


    /**
     * @description: 业务异常处理
     * @author: chenhuan
     * @date: 2021/5/28
     * @version V1.0
     **/
    @ExceptionHandler(value = BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public GlobalResponse businessExceptionHandler(BusinessException exception) {
        log.error("BusinessException:", exception);
        return GlobalResponse.fail(exception.getMessage());
    }

    /**
     * @description: 未知异常处理
     * @author: chenhuan
     * @date: 2021/5/28
     * @version V1.0
     **/
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public GlobalResponse exceptionHandler(Exception exception) {
        log.error("Exception:", exception);
        //获取异常类型
        Class excClass = exception.getClass();
        //1、判断NullPointerException异常类型
        if (ClassUtils.isAssignable(excClass, NullPointerException.class)) {
            return GlobalResponse.fail("系统空指针,请联系后端人员！");
        }

        //2、判断SQLException异常类型
        if (ClassUtils.isAssignable(excClass, SQLException.class)) {
            return GlobalResponse.fail("操作数据库异常,请联系后端人员！");
        }

        //3、判断MysqlDataTruncation异常类型
        if (exception.getMessage().contains("Data too long")) {
            return GlobalResponse.fail("数据库字段超长,请联系后端人员！").setData(exception.getMessage());
        }

        //3、其他异常类型
        return GlobalResponse.fail("系统异常[" + exception.getMessage() + "],请联系后端人员！");
    }

}
